﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace GtsToFlac3d
{
	public partial class MainForm : Form
	{
		public MainForm()
		{
			InitializeComponent();
		}

		private string _nodeFilePath;
		private string _elemFilePath;
		private string _outputFilePath;

		private void textBoxNodeFilePath_Click(object sender, EventArgs e)
		{
			var ofd = new OpenFileDialog()
			{
				DefaultExt = "txt",
				Multiselect = false,
				Filter = "txt文件|*.txt"
			};
			if (ofd.ShowDialog() == DialogResult.OK)
			{
				_nodeFilePath = ofd.FileName;
				textBoxNodeFilePath.Text = _nodeFilePath;
			}
		}

		private void textBoxElemFilePath_Click(object sender, EventArgs e)
		{
			var ofd = new OpenFileDialog()
			{
				DefaultExt = "txt",
				Multiselect = false,
				Filter = "txt文件|*.txt"
			};
			if (ofd.ShowDialog() == DialogResult.OK)
			{
				_elemFilePath = ofd.FileName;
				textBoxElemFilePath.Text = _elemFilePath;

				_outputFilePath = _elemFilePath + ".f3grid";
				textBoxOutputFilePath.Text = _outputFilePath;
			}
		}

		private void buttonConvert_Click(object sender, EventArgs e)
		{
			if (string.IsNullOrEmpty(_nodeFilePath) || string.IsNullOrEmpty(_elemFilePath))
			{
				MessageBox.Show("Speficy the node and elem file path first!");
				return;
			}

			//
			// Parse node data
			//
			var dataLines = File.ReadLines(_nodeFilePath).Where(line => line.Trim().Length > 0);
			var nodeData = dataLines.Select(ParseNodeDataLine);

			//
			// Parse element and element set data
			//
			var elemData = new List<Elem>();
			var elemSetData = new Dictionary<string, List<int>>();
			dataLines = File.ReadLines(_elemFilePath).Where(line => line.Trim().Length > 0);
			foreach (var dataLine in dataLines)
			{
				(var elem, var elemSetName) = ParseElemDataLine(dataLine);
				elemData.Add(elem);
				if (elemSetData.ContainsKey(elemSetName))
				{
					elemSetData[elemSetName].Add(elem.Id);
				}
				else
				{
					elemSetData.Add(elemSetName, new List<int>());
				}
			}

			//
			// Output
			//
			var outputLines = new List<string>();

			// Output node data
			outputLines.Add("*GRIDPOINTS");
			foreach (var node in nodeData)
			{
				var id = node.Id;
				var x = node.X;
				var y = node.Y;
				var z = node.Z;
				outputLines.Add($"G {id},{x:E3},{y:E3},{z:E3}");
			}

			// Output elem data
			outputLines.Add("*ZONES");
			var C3D8ElemData = elemData.Where(elem => elem.NodeIds.Count == 8);
			foreach (var elem in C3D8ElemData)
			{
				Debug.Assert(elem.NodeIds.Count == 8);
				var id = elem.Id;
				var p0 = elem.NodeIds[0];
				var p1 = elem.NodeIds[1];
				var p2 = elem.NodeIds[3];
				var p3 = elem.NodeIds[4];
				var p4 = elem.NodeIds[2];
				var p5 = elem.NodeIds[7];
				var p6 = elem.NodeIds[5];
				var p7 = elem.NodeIds[6];
				outputLines.Add($"Z B8 {id},{p0},{p1},{p2},{p3},{p4},{p5},{p6},{p7}");
			}

			var C3D6ElemData = elemData.Where(elem => elem.NodeIds.Count == 6);
			foreach (var elem in C3D6ElemData)
			{
				Debug.Assert(elem.NodeIds.Count == 6);
				var id = elem.Id;
				var p0 = elem.NodeIds[0];
				var p1 = elem.NodeIds[2];
				var p2 = elem.NodeIds[3];
				var p3 = elem.NodeIds[1];
				var p4 = elem.NodeIds[5];
				var p5 = elem.NodeIds[4];
				outputLines.Add($"Z W6 {id},{p0},{p1},{p2},{p3},{p4},{p5}");
			}

			var C3D4ElemData = elemData.Where(elem => elem.NodeIds.Count == 4);
			foreach (var elem in C3D4ElemData)
			{
				Debug.Assert(elem.NodeIds.Count == 4);
				var id = elem.Id;
				var p0 = elem.NodeIds[0];
				var p1 = elem.NodeIds[1];
				var p2 = elem.NodeIds[2];
				var p3 = elem.NodeIds[3];
				outputLines.Add($"Z T4 {id},{p0},{p1},{p2},{p3}");
			}

			// Output elem set data
			outputLines.Add("*GROUPS");
			var setIdLine = new StringBuilder();
			foreach (var elemSet in elemSetData)
			{
				outputLines.Add($"ZGROUP \'{elemSet.Key}\'");
				const int idCountPerLine = 15;
				var idCount = 0;
				foreach (var elemId in elemSet.Value)
				{
					setIdLine.Append($"{elemId} ");
					idCount++;
					if (idCount >= idCountPerLine)
					{
						setIdLine.Append(Environment.NewLine);
						idCount = 0;
					}
				}
				outputLines.Add(setIdLine.ToString());

				setIdLine.Clear();
			}

			// Write to flac3d mesh file
			File.WriteAllLines(_outputFilePath, outputLines);

			MessageBox.Show("Done!");
		}

		#region Helper methods
		public static Node ParseNodeDataLine(string dataLine)
		{
			var tokens = dataLine.Trim().Split('\t');
			Debug.Assert(tokens.Length == 5);
			var id = int.Parse(tokens[0].Trim());
			var x = double.Parse(tokens[2].Trim());
			var y = double.Parse(tokens[3].Trim());
			var z = double.Parse(tokens[4].Trim());
			return new Node(id, x, y, z);
		}

		public static (Elem Elem, string ElemSetName) ParseElemDataLine(string dataLine)
		{
			var tokens = dataLine.Trim().Split('\t').ToList();
			Debug.Assert(tokens.Count >= 7);
			var id = int.Parse(tokens[0].Trim());
			var nodeIds = tokens.GetRange(3, tokens.Count - 3)
				.Select(token => int.Parse(token.Trim())).ToList();
			var elemSetName = tokens[2].Split(':')[1];
			return (new Elem(id, nodeIds), elemSetName);
		}
		#endregion
	}

	#region FEM primetives
	public class Node
	{
		public Node(int id, double x, double y, double z)
		{
			Id = id;
			X = x;
			Y = y;
			Z = z;
		}

		public int Id { get; }
		public double X { get; }
		public double Y { get; }
		public double Z { get; }
	}

	public class Elem
	{
		public Elem(int id, List<int> nodeIds)
		{
			this.Id = id;
			this.NodeIds = nodeIds;
		}

		public int Id { get; }
		public List<int> NodeIds { get; }
	}
	#endregion
}
